home *** CD-ROM | disk | FTP | other *** search
/ Celestin Apprentice 5 / Apprentice-Release5.iso / Source Code / Add-Ons / MPW / MPW dmake 4.0 / getinp.c < prev    next >
Text File  |  1995-06-04  |  15KB  |  539 lines

  1. /* RCS      -- $Header: /u5/dvadura/src/public/dmake/src/RCS/getinp.c,v 1.1 1994/10/06 17:41:59 dvadura Exp $
  2. -- SYNOPSIS -- handle reading of input.
  3. -- 
  4. -- DESCRIPTION
  5. --    The code in this file reads the input from the specified stream
  6. --    into the provided buffer of size Buffer_size.  In doing so it deletes
  7. --    comments.  Comments are delimited by the #, and
  8. --    <nl> character sequences.  An exception is \# which
  9. --    is replaced by # in the input.  Line continuations are signalled
  10. --    at the end of a line and are recognized inside comments.
  11. --    The line continuation is always  <\><nl>.
  12. --
  13. --    If the file to read is NIL(FILE) then the Get_line routine returns the
  14. --    next rule from the builtin rule table if there is one.
  15. --
  16. -- AUTHOR
  17. --      Dennis Vadura, dvadura@watdragon.uwaterloo.ca
  18. --      CS DEPT, University of Waterloo, Waterloo, Ont., Canada
  19. --
  20. -- COPYRIGHT
  21. --      Copyright (c) 1992,1994 by Dennis Vadura.  All rights reserved.
  22. -- 
  23. --      This program is free software; you can redistribute it and/or
  24. --      modify it under the terms of the GNU General Public License
  25. --      (version 1), as published by the Free Software Foundation, and
  26. --      found in the file 'LICENSE' included with this distribution.
  27. -- 
  28. --      This program is distributed in the hope that it will be useful,
  29. --      but WITHOUT ANY WARRANTY; without even the implied warrant of
  30. --      MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  31. --      GNU General Public License for more details.
  32. -- 
  33. --      You should have received a copy of the GNU General Public License
  34. --      along with this program;  if not, write to the Free Software
  35. --      Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
  36. --
  37. -- LOG
  38. --     $Log: getinp.c,v $
  39.  * Revision 1.1  1994/10/06  17:41:59  dvadura
  40.  * dmake Release Version 4.0, Initial revision
  41.  *
  42. */
  43.  
  44. #include "extern.h"
  45.  
  46. #define IS_WHITE(A)  ((A == ' ') || (A == '\t') || (A == '\n') || (A == '\r'))
  47. #define SCAN_WHITE(A) \
  48.     while( IS_WHITE(*A) ) A++;
  49.  
  50. static    int    _is_conditional ANSI((char*));
  51. static    int    _handle_conditional ANSI((int, TKSTRPTR));
  52.  
  53. static int  rule_ind = 0;    /* index of rule when reading Rule_tab     */
  54. static int  skip = FALSE;    /* if true the skip input         */
  55.  
  56.  
  57. PUBLIC int
  58. Get_line( buf, fil )/*
  59. ======================
  60.         Read a line of input from the file stripping
  61.         off comments.  The routine returns TRUE if EOF */
  62. char *buf;
  63. FILE *fil;
  64. {
  65.    extern   char **Rule_tab;
  66.    register char *p;
  67.    register char *c;
  68.    char      *q;
  69.    char             *buf_org;
  70.    static   int     ignore = FALSE;
  71.    int          cont   = FALSE;
  72.    int          pos    = 0;
  73.    int         res;
  74.  
  75.    DB_ENTER( "Get_line" );
  76.  
  77.    if( Skip_to_eof ) {
  78.       Skip_to_eof = FALSE;
  79.       rule_ind    = 0;
  80.  
  81.       if( Verbose & V_MAKE )
  82.      Warning("Ignoring remainder of file %s", Filename());
  83.  
  84.       DB_RETURN(TRUE);
  85.    }
  86.  
  87.    if( fil == NIL(FILE) ) {
  88.       /* Reading the internal rule table.  Set the rule_index to zero.
  89.        * This way ReadEnvironment works as expected every time. */
  90.  
  91.       while( (p = Rule_tab[ rule_ind++ ]) != NIL(char) )
  92.      /* The last test in this if '*p != '~', handles the environment
  93.       * passing conventions used by MKS to pass arguments.  We want to
  94.       * skip those environment entries. */
  95.      if( !Readenv || (Readenv && (strchr(p,'=') != NIL(char)) && *p!='~')){
  96.         strcpy( buf, p );
  97.  
  98.         DB_PRINT( "io", ("Returning [%s]", buf) );
  99.         DB_RETURN( FALSE );
  100.      }
  101.  
  102.       rule_ind = 0;
  103.  
  104.       DB_PRINT( "io", ("Done Ruletab") );
  105.       DB_RETURN( TRUE );
  106.    }
  107.  
  108.    buf_org = buf;
  109.  
  110. do_again:
  111.    do {
  112.       p = buf+pos;
  113.       if(feof( fil ) || (fgets( p, Buffer_size-pos, fil ) == NIL(char)))
  114.      DB_RETURN( TRUE );
  115.  
  116.       Line_number++;
  117.  
  118.       /* ignore input if ignore flag set and line ends in a continuation
  119.      character. */
  120.       q = p+strlen(p)-2;
  121.       if( q<p ) q=p;
  122.  
  123.       /* ignore each RETURN at the end of a line before any further
  124.        * processing */
  125.       if( q[0] == '\r' && q[1] == '\n' ) {
  126.      q[0] = '\n';
  127.      q[1] = '\0';
  128.      q--;
  129.       }
  130.       /* you also have to deal with END_OF_FILE chars to process raw
  131.        * DOS-Files. Normally they are the last chars in file, but after
  132.        * working on these file with vi, there is an additional NEWLINE
  133.        * after the last END_OF_FILE. So if the second last char in the
  134.        * actual line is END_OF_FILE, you can skip the last char. Then
  135.        * you can search the line back until you find no more END_OF_FILE
  136.        * and nuke each you found by string termination. */
  137.       if( q[0] == '\032' )
  138.      q--;
  139.       while( q[1] == '\032' ) {
  140.      q[1] = '\0';
  141.      q--;
  142.       }
  143.  
  144.       if( ignore ) {
  145.      if( q[0] != CONTINUATION_CHAR || q[1] != '\n' )  ignore = FALSE;
  146.      *p = '\0';
  147.      continue;
  148.       }
  149.  
  150.       c = Do_comment(p, &q, Group || (*buf == '\t') || (Notabs && *buf ==' '));
  151.       
  152.       /* Does the end of the line end in a continuation sequence? */
  153.       
  154.       if( (q[0] == CONTINUATION_CHAR) && (q[1] == '\n')) {
  155.      /* If the continuation was at the end of a comment then ignore the
  156.       * next input line, (or lines until we get one ending in just <nl>)
  157.       * else it's a continuation, so build the input line from several
  158.       * text lines on input.  The maximum size of this is governened by
  159.       * Buffer_size */
  160.      if( q != p && q[-1] == CONTINUATION_CHAR ) {
  161.         strcpy( q, q+1 );
  162.         q--;
  163.         cont = FALSE;
  164.      }
  165.      else if( c != NIL(char) )
  166.         ignore = TRUE;
  167.      else
  168.         cont   = TRUE;
  169.       }
  170.       else {
  171.      cont = FALSE;
  172.       }
  173.  
  174.       q    = ( c == NIL(char) ) ? q+2 : c;
  175.       pos += q-p;
  176.    }
  177.    while( (cont || !*buf) && (pos <= Buffer_size) );
  178.  
  179.    if( buf[ pos-1 ] == '\n' )
  180.       buf[ --pos ] = '\0';
  181.    else
  182.       if( pos == Buffer_size-1 )
  183.      Fatal( "Input line too long, increase MAXLINELENGTH" );
  184.  
  185.  
  186.    /* Now that we have the next line of input to make, we should check to
  187.     * see if it is a conditional expression.  If it is then process it,
  188.     * otherwise pass it on to the parser. */
  189.  
  190.    if( *(p = DmStrSpn(buf, " \t\r\n")) == CONDSTART ) {
  191.       TKSTR token;
  192.  
  193.       SET_TOKEN( &token, p );
  194.  
  195.       p = Get_token( &token, "", FALSE );
  196.  
  197.       if( (res = _is_conditional(p)) != 0 )    /* ignore non control special */
  198.       {                        /* targets               */
  199.      res  = _handle_conditional( res, &token );
  200.      skip = TRUE;
  201.       }
  202.       else {
  203.      CLEAR_TOKEN( &token );
  204.      res  = TRUE;
  205.       }
  206.    }
  207.  
  208.    if( skip ) {
  209.       buf  = buf_org;        /* ignore line just read in */
  210.       pos  = 0;
  211.       skip = res;
  212.       goto do_again;
  213.    }
  214.  
  215.    DB_PRINT( "io", ("Returning [%s]", buf) );
  216.    DB_RETURN( FALSE );
  217. }
  218.  
  219.  
  220. PUBLIC char *
  221. Do_comment(str, pend, keep)/*
  222. =============================
  223.    Search the input string looking for comment chars.  If it contains
  224.    comment chars then NUKE the remainder of the line, if the comment
  225.    char is preceeded by \ then shift the remainder of the line left
  226.    by one char. */
  227. char *str;
  228. char **pend;
  229. int  keep;
  230. {
  231.    char *c = str;
  232.  
  233.    while( (c = strchr(c, COMMENT_CHAR)) != NIL(char) ) {
  234.       if( Comment || State == NORMAL_SCAN )
  235.      if( c != str && c[-1] == ESCAPE_CHAR ) {
  236.         strcpy( c-1, c );        /* copy it left, due to \# */
  237.         if( pend ) (*pend)--;    /* shift tail pointer left */
  238.      }
  239.      else {
  240.         if(    !No_exec
  241.         && c == str
  242.             && c[1] == '!'
  243.             && Line_number == 1
  244.         && Nestlevel() == 1 ) {
  245.            char *cmnd;
  246.          
  247.            cmnd = Expand(c+2);
  248.            cmnd[strlen(cmnd)-1] = '\0';    /* strip last newline */
  249.            Current_target = Root;
  250.            Swap_on_exec = TRUE;
  251.            Wait_for_completion = TRUE;
  252.            Do_cmnd(cmnd, FALSE, TRUE, Current_target, FALSE, FALSE, TRUE);
  253.         }
  254.  
  255.         *c = '\0';               /* a true comment so break */
  256.         break;
  257.      }
  258.       else {
  259.          if( keep )
  260.         c = NIL(char);
  261.      else
  262.         *c = '\0';
  263.  
  264.      break;
  265.       }
  266.    }
  267.  
  268.    return(c);
  269. }
  270.       
  271.  
  272. PUBLIC char *
  273. Get_token( string, brk, anchor )/*
  274. ==================================
  275.     Return the next token in string.
  276.     Returns empty string when no more tokens in string.
  277.     brk is a list of chars that also cause breaks in addition to space and
  278.     tab, but are themselves returned as tokens.  if brk is NULL then the
  279.     remainder of the line is returned as a single token.
  280.     
  281.     anchor if 1, says break on chars in the brk list, but only if
  282.     the entire token begins with the first char of the brk list, if
  283.     0 then any char of brk will cause a break to occurr.
  284.     
  285.     If anchor is 2, then break only seeing the first char in the break
  286.     list allowing only chars in the break list to form the prefix. */
  287.  
  288. TKSTRPTR  string;
  289. char      *brk;
  290. int      anchor;
  291. {
  292.    register char *s;
  293.    register char *curp;
  294.    register char *t;
  295.    int           done = FALSE;
  296.    char          space[20];
  297.  
  298.    DB_ENTER( "Get_token" );
  299.  
  300.    s  = string->tk_str;              /* Get string parameters    */
  301.    *s = string->tk_cchar;          /* ... and strip leading w/s    */
  302.  
  303.    SCAN_WHITE( s );
  304.  
  305.    DB_PRINT( "tok", ("What's left [%s]", s) );
  306.  
  307.    if( !*s ) {
  308.       DB_PRINT( "tok", ("Returning NULL token") );
  309.       DB_RETURN( "" );
  310.    }
  311.  
  312.  
  313.    /* Build the space list.  space contains all those chars that may possibly
  314.     * cause breaks.  This includes the brk list as well as white space. */
  315.  
  316.    if( brk != NIL(char) ) {
  317.       strcpy( space, " \t\r\n" );
  318.       strcat( space, brk   );
  319.    }
  320.    else {
  321.       space[0] = 0xff;            /* a char we know will not show up      */
  322.       space[1] = 0;
  323.    }
  324.  
  325.  
  326.    /* Handle processing of quoted tokens.  Note that this is disabled if
  327.     * brk is equal to NIL */
  328.  
  329.    while( *s == '\"' && ((brk != NIL(char)) || !string->tk_quote) ) {
  330.       s++;
  331.       if( string->tk_quote ) {
  332.      curp = s-1;
  333.      do { curp = strchr( curp+1, '\"' ); }
  334.      while( (curp != NIL(char)) && (*(curp+1) == '\"'));
  335.  
  336.          if( curp == NIL(char) ) Fatal( "Unmatched quote in token" );
  337.      string->tk_quote = !string->tk_quote;
  338.  
  339.      /* Check for "" case, and if found ignore it */
  340.      if( curp == s ) continue;
  341.      goto found_token;
  342.       }
  343.       else
  344.      SCAN_WHITE( s );
  345.  
  346.       string->tk_quote = !string->tk_quote;
  347.    }
  348.    
  349.  
  350.    /* Check for a token break character at the beginning of the token.
  351.     * If found return the next set of break chars as a token. */
  352.  
  353.    if( anchor == 2 && brk != NIL(char) ) {
  354.       curp = s;
  355.       while( *curp && (strchr(brk,*curp)!=NIL(char)) && (*curp!=*brk) ) curp++;
  356.       done = (*brk == *curp++);
  357.    }
  358.    else if( (brk != NIL(char)) && (strchr( brk, *s ) != NIL(char)) ) {
  359.       curp = DmStrSpn( s, brk );
  360.       done = (anchor == 0) ? TRUE :
  361.          ((anchor == 1)?(*s == *brk) : (*brk == curp[-1]));
  362.    }
  363.  
  364.  
  365.    /* Scan for the next token in the list and return it less the break char
  366.     * that was used to terminate the token.  It will possibly be returned in
  367.     * the next call to Get_token */
  368.  
  369.    if( !done ) {
  370.       SCAN_WHITE( s );
  371.  
  372.       t = s;
  373.       do {
  374.      done = TRUE;
  375.      curp = DmStrPbrk(t, space);
  376.      
  377.      if( anchor && *curp && !IS_WHITE( *curp ) )
  378.         if( ((anchor == 1)?*curp:DmStrSpn(curp,brk)[-1]) != *brk ) {
  379.            t++;
  380.            done = FALSE;
  381.         }
  382.       }
  383.       while( !done );
  384.  
  385.       if( (curp == s) && (strchr(brk, *curp) != NIL(char)) ) curp++;
  386.    }
  387.  
  388. found_token:
  389.    string->tk_str   = curp;
  390.    string->tk_cchar = *curp;
  391.    *curp = '\0';
  392.  
  393.    DB_PRINT( "tok", ("Returning [%s]", s) );
  394.    DB_RETURN( s );
  395. }
  396.  
  397.  
  398. static int
  399. _is_conditional( tg )/*
  400. =======================
  401.     Look at tg and return it's value if it is a conditional identifier
  402.     otherwise return 0. */
  403. char *tg;
  404. {
  405.    DB_ENTER( "_is_conditional" );
  406.    
  407.    tg++;
  408.    switch( *tg ) {
  409.       case 'I': if( !strcmp( tg, "IF" )) DB_RETURN( ST_IF   ); break;
  410.       
  411.       case 'E':
  412.          if( !strcmp( tg, "END" ))     DB_RETURN( ST_END  );
  413.          else if( !strcmp( tg, "ENDIF")) DB_RETURN( ST_END  );
  414.          else if( !strcmp( tg, "ELSE" )) DB_RETURN( ST_ELSE );
  415.          else if( !strcmp( tg, "ELIF" )) DB_RETURN( ST_ELIF );
  416.      break;
  417.    }
  418.    
  419.    DB_RETURN( 0 );
  420. }
  421.  
  422.  
  423.  
  424. #define SEEN_END  0x00
  425. #define SEEN_IF   0x01
  426. #define SEEN_ELSE 0x02
  427. #define SEEN_ELIF 0x04
  428.  
  429. #define ACCEPT_IF   0x10
  430. #define ACCEPT_ELIF 0x20
  431.  
  432. static int
  433. _handle_conditional( opcode, tg )/*
  434. ===================================
  435.     Perform the necessary processing for .IF conditinal targets.
  436.     Someday this should be modified to do bracketted expressions ala
  437.     CPP... sigh */
  438. int      opcode;
  439. TKSTRPTR tg;
  440. {
  441.    static short    action[MAX_COND_DEPTH];
  442.    static char  ifcntl[MAX_COND_DEPTH];
  443.    char     *tok, *lhs, *rhs, *op, *expr;
  444.    int      result;
  445.  
  446.    DB_ENTER( "_handle_conditional" );
  447.  
  448.    switch( opcode ) {
  449.       case ST_ELIF:
  450.          if( !(ifcntl[Nest_level] & SEEN_IF) || (ifcntl[Nest_level]&SEEN_ELSE) )
  451.         Fatal(".ELIF without a preceeding .IF" );
  452.      /*FALLTHRU*/
  453.  
  454.       case ST_IF:
  455.      if( opcode == ST_IF && (Nest_level+1) == MAX_COND_DEPTH )
  456.         Fatal( ".IF .ELSE ... .END nesting too deep" );
  457.  
  458.      If_expand = TRUE;
  459.      expr = Expand( Get_token( tg, NIL(char), FALSE ));
  460.      If_expand = FALSE;
  461.      lhs = DmStrSpn( expr, " \t" );
  462.      if( !*lhs ) lhs = NIL(char);
  463.  
  464.      if( (op = DmStrStr( lhs, "==" )) == NIL(char) )
  465.         op = DmStrStr( lhs, "!=" );
  466.  
  467.      if( op == NIL(char) )
  468.         result = (lhs != NIL(char));
  469.      else {
  470.         op[1] = op[0];
  471.         if( lhs != op ) {
  472.            for( tok = op-1; (tok != lhs) && ((*tok == ' ')||(*tok == '\t'));
  473.                 tok-- );
  474.            tok[1] = '\0';
  475.         }
  476.         else
  477.            lhs = NIL(char);
  478.  
  479.         op++;
  480.         rhs = DmStrSpn( op+1, " \t" );
  481.         if( !*rhs ) rhs = NIL(char);
  482.  
  483.         if( (rhs == NIL(char)) || (lhs == NIL(char)) )
  484.            result = (rhs == lhs) ? TRUE : FALSE;
  485.         else {
  486.            tok = rhs + strlen( rhs );
  487.            for( tok=tok-1; (tok != lhs) && ((*tok == ' ')||(*tok == '\t'));
  488.             tok--);
  489.            tok[1] = '\0';
  490.  
  491.            result = (strcmp( lhs, rhs ) == 0) ? TRUE : FALSE;
  492.         }
  493.  
  494.         if( *op == '!' ) result = !result;
  495.      }
  496.  
  497.      if( expr != NIL(char) ) FREE( expr );
  498.  
  499.      if( opcode == ST_IF ) {
  500.         Nest_level++;
  501.         action[Nest_level] = 1;
  502.      }
  503.      ifcntl[Nest_level] |= (opcode==ST_IF)?SEEN_IF:SEEN_ELIF;
  504.  
  505.      if( result ) {
  506.         if( !(ifcntl[Nest_level] & (ACCEPT_IF|ACCEPT_ELIF)) ) {
  507.            action[ Nest_level ] = action[ Nest_level-1 ];
  508.            ifcntl[Nest_level] |= (opcode==ST_IF)?ACCEPT_IF:ACCEPT_ELIF;
  509.         }
  510.         else
  511.            action[Nest_level] = 1;
  512.      }
  513.      else
  514.         action[Nest_level] = 1;
  515.      break;
  516.  
  517.       case ST_ELSE:
  518.      if( Nest_level <= 0 ) Fatal( ".ELSE without .IF" );
  519.      if( ifcntl[Nest_level] & SEEN_ELSE )
  520.         Fatal( "Missing .IF or .ELIF before .ELSE" );
  521.  
  522.      if( ifcntl[Nest_level] & (ACCEPT_IF|ACCEPT_ELIF) )
  523.         action[Nest_level] = 1;
  524.      else if( action[ Nest_level-1 ] != 1 )
  525.         action[ Nest_level ] ^= 0x1;    /* flip between 0 and 1    */
  526.  
  527.      ifcntl[Nest_level] |= SEEN_ELSE;
  528.      break;
  529.  
  530.       case ST_END:
  531.      ifcntl[Nest_level] = SEEN_END;
  532.      Nest_level--;
  533.      if( Nest_level < 0 ) Fatal( "Unmatched .END[IF]" );
  534.      break;
  535.    }
  536.  
  537.    DB_RETURN( action[ Nest_level ] );
  538. }
  539.